﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;

namespace SolutionSettings {
    class ActivityLogTraceListener : TraceListener {
        private IVsActivityLog ActivityLog;
        private StringBuilder WriteBuffer;
        private int LogToActivityLog_RecursionCount;

        public string SourceName { get; set; }

        public ActivityLogTraceListener(IServiceProvider provider)
            : this(provider.GetService(typeof(SVsActivityLog)) as IVsActivityLog) { }

        public ActivityLogTraceListener(IVsActivityLog activityLog) {
            ActivityLog = activityLog;
            WriteBuffer = new StringBuilder();
            LogToActivityLog_RecursionCount = 0;
        }

        void LogToActivityLog(__ACTIVITYLOG_ENTRYTYPE type, string message, string path, int? hr) {
            if (ActivityLog == null || LogToActivityLog_RecursionCount > 0) {
                return;
            }

            uint uiType = (uint)type;
            int logHR = 0;
            if (path != null) {
                if (hr != null) {
                    logHR = ActivityLog.LogEntryHrPath(uiType, SourceName, message, hr ?? 0, path);
                } else {
                    logHR = ActivityLog.LogEntryPath(uiType, SourceName, message, path);
                }
            } else {
                if (hr != null) {
                    logHR = ActivityLog.LogEntryHr(uiType, SourceName, message, hr ?? 0);
                } else {
                    logHR = ActivityLog.LogEntry(uiType, SourceName, message);
                }
            }

            if (!ErrorHandler.Succeeded(logHR)) {
                Interlocked.Increment(ref LogToActivityLog_RecursionCount);
                Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture,
                    "{0}: Writing to the activity log failed with HR = {1} ({1:X})",
                    SourceName, logHR, logHR));
                Interlocked.Decrement(ref LogToActivityLog_RecursionCount);
            }
        }

        public void Log(string message, string path = null, int? hr = null) {
            LogToActivityLog(__ACTIVITYLOG_ENTRYTYPE.ALE_INFORMATION, message, path, hr);
        }

        public void LogWarning(string message, string path = null, int? hr = null) {
            LogToActivityLog(__ACTIVITYLOG_ENTRYTYPE.ALE_WARNING, message, path, hr);
        }

        public void LogError(string message, string path = null, int? hr = null) {
            LogToActivityLog(__ACTIVITYLOG_ENTRYTYPE.ALE_ERROR, message, path, hr);
        }

        public override void Fail(string message) {
            LogError(message);
        }

        public override void Fail(string message, string detailMessage) {
            this.Fail(message + "\n\n" + detailMessage);
        }

        public override void Write(string message) {
            int lineBreak = message.IndexOf('\n');
            if (lineBreak == -1) {
                WriteBuffer.Append(message);
            } else if (lineBreak == message.Length - 1) {
                WriteBuffer.Append(message);
                Log(WriteBuffer.ToString());
                WriteBuffer.Clear();
            } else {
                WriteBuffer.Append(message.Substring(0, lineBreak));
                Log(WriteBuffer.ToString());
                WriteBuffer.Clear();
                this.Write(message.Substring(lineBreak + 1));
            }
        }

        public override void WriteLine(string message) {
            this.Write(message + '\n');
        }
    }

    class NoInformationTraceFilter : TraceFilter {
        public override bool ShouldTrace(TraceEventCache cache, string source, TraceEventType eventType, int id, string formatOrMessage, object[] args, object data1, object[] data) {
            return eventType == TraceEventType.Warning ||
                eventType == TraceEventType.Error ||
                eventType == TraceEventType.Critical;
        }
    }
}
